#pragma once
#include "requestor.hpp"
#include <unordered_set>

namespace rpc
{
    namespace client
    {
        class Provider
        {
        public:
            using ptr = std::shared_ptr<Provider>;
            Provider(const Requestor::ptr &requestor) : _requestor(requestor)
            {}
            bool registryMethod(const BaseConnection::ptr& conn,const std::string& method,const Address& host)
            {
                auto req_msg = MessageFactory::create<ServiceRequest>();
                req_msg->setid(UUID::uuid());
                req_msg->setMethod(method);
                req_msg->setHost(host);
                req_msg->setmtype(Mtype::REQ_SERVICE);
                req_msg->setOptype(ServiceOptype::SERVICE_REGISTRY);
                BaseMessage::ptr rsp_msg;
                bool ret = _requestor->send(conn,req_msg,rsp_msg);
                if(ret == false)
                {
                    ELOG("%s 服务注册失败！", method.c_str());
                    return false;
                }
                auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(rsp_msg);
                if(service_rsp.get() == nullptr)
                {
                    ELOG("响应类型向下转换失败！");
                    return false;
                }
                if(service_rsp->rcode() == Rcode::RCODE_OK)
                {
                    ELOG("服务注册失败，原因：%s", errReason(service_rsp->rcode()).c_str());
                    return false;
                }
                return true;
            }
        private:
            Requestor::ptr _requestor;
        };
        class MethodHost
        {
        public:
            using ptr = std::shared_ptr<MethodHost>;
            MethodHost() : _idx(0) {}
            MethodHost(const std::vector<Address>& hosts) 
            : _hosts(hosts.begin(),hosts.end()),_idx(0)
            {}
            void appendHost(const Address& host)
            {
                //中途收到服务上线请求后被调用
                std::unique_lock<std::mutex> lock(_mutex);
                _hosts.push_back(host);
            }
            void removeHost(const Address& host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                for(auto it = _hosts.begin() ; it != _hosts.end();it++)
                {
                    if(host == *it)
                    {
                        _hosts.erase(it);
                        break;
                    }
                }
            }
            Address chooseHost() {
                std::unique_lock<std::mutex> lock(_mutex);
                size_t pos = _idx++ % _hosts.size();
                return _hosts[pos];
            }
            bool empty() {
                std::unique_lock<std::mutex> lock(_mutex);
                return _hosts.empty();
            }
        private:
            std::mutex _mutex;
            size_t _idx;
            std::vector<Address> _hosts;
        };

        class Discoverer
        {
        public:
            using ptr = std::shared_ptr<Discoverer>;
            using OfflineCallback = std::function<void(const Address&)>;
            Discoverer(const Requestor::ptr& requestor,const OfflineCallback& cb)
            : _requestor(requestor),
            _offline_callback(cb)
            {}
            bool serviceDiscover(const BaseConnection::ptr& conn,const std::string& method,Address& host)
            {
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _method_hosts.find(method);
                    if(it != _method_hosts.end())
                    {
                        if(it->second->empty() == false)
                        {
                            host = it->second->chooseHost();
                            return true;
                        }
                    }
                }
                //没有服务提供者
                auto msg_req = MessageFactory::create<ServiceRequest>();
                msg_req->setid(UUID::uuid());
                msg_req->setMethod(method);
                msg_req->setmtype(Mtype::REQ_SERVICE);
                msg_req->setOptype(ServiceOptype::SERVICE_DISCOVERY);;
                BaseMessage::ptr msg_rsp;
                bool ret = _requestor->send(conn,msg_req,msg_rsp);
                if(ret == false)
                {
                    ELOG("服务发现失败");
                    return false;
                }
                auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                if(service_rsp.get() == nullptr)
                {
                    ELOG("服务发现失败！响应类型转换失败！");
                    return false;
                }
                if(service_rsp->rcode() != Rcode::RCODE_OK)
                {
                    ELOG("服务发现失败！%s", errReason(service_rsp->rcode()).c_str());
                    return false;
                }
                
                std::unique_lock<std::mutex> lock(_mutex);
                auto method_host = std::make_shared<MethodHost>(service_rsp->host());
                if(method_host->empty())
                {
                    ELOG("%s 服务发现失败！没有能够提供服务的主机！", method.c_str());
                    return false;
                }
                host = method_host->chooseHost();
                _method_hosts[method] = method_host;

                return true;
            }

            void onServiceRequest(const BaseConnection::ptr& conn,const ServiceRequest::ptr& msg)
            {
                auto optype = msg->optype();
                std::string method = msg->method();
                Address host = msg->host();
                std::unique_lock<std::mutex> lock(_mutex);
                if(optype == ServiceOptype::SERVICE_ONLINE)
                {
                    //上线通知，新增服务主机和方法的映射
                    auto it = _method_hosts.find(method);
                    if(it == _method_hosts.end())
                    {
                        //代表当前没有提供这个服务的主机
                        auto method_host = std::make_shared<MethodHost>();
                        method_host->appendHost(host);
                        _method_hosts.insert(std::make_pair(method,method_host));
                    }
                    else{
                        it->second->appendHost(host);
                    }
                }
                else
                {
                    auto it = _method_hosts.find(method);
                    if(it == _method_hosts.end())
                    {
                        return;
                    }
                    else{
                        it->second->removeHost(host);
                        _offline_callback(host);
                    }
                }
            }
        private:
            std::mutex _mutex;
            OfflineCallback _offline_callback;
            std::unordered_map<std::string,MethodHost::ptr> _method_hosts;
            Requestor::ptr _requestor;
        };
    }
}